package org.example.task.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtUtil {
    // 从配置文件中注入密钥字符串
    @Value("${jwt.secret-key}")
    private String secret;

    // 从配置文件中注入令牌有效期（毫秒）
    @Value("${jwt.expiration}")
    private Long expiration;

    // 加密密钥对象（基于HMAC-SHA算法）
    private SecretKey key;

    /**
     * 初始化方法：将配置的密钥字符串转换为加密密钥
     * 使用 @PostConstruct 确保在依赖注入完成后执行
     */
    @PostConstruct
    public void init() {
        // 将字符串密钥转换为符合HMAC-SHA规范的密钥对象
        this.key = Keys.hmacShaKeyFor(secret.getBytes());
    }

    /**
     * 生成JWT访问令牌（包含自定义声明）
     *
     * @param subject  令牌主题（通常存储用户唯一标识，如用户ID）
     * @param claims   自定义声明（键值对形式存储额外信息，如角色、权限等）
     * @return         签名后的JWT字符串
     */
    public String generateToken(String subject, Map<String, Object> claims) {
        return Jwts.builder()
                .claims(claims)        // 设置自定义声明
                .subject(subject)       // 设置主题
                .issuedAt(new Date())   // 设置签发时间
                .expiration(new Date(System.currentTimeMillis() + expiration))  // 设置过期时间
                .signWith(key)          // 使用密钥签名
                .compact();             // 生成紧凑型URL安全字符串
    }

    /**
     * 生成JWT访问令牌（不含自定义声明）
     *
     * @param subject 令牌主题
     * @return        签名后的JWT字符串
     */
    public String generateToken(String subject) {
        // 使用空Map创建无自定义声明的令牌
        return generateToken(subject, new HashMap<>());
    }

    /**
     * 解析并验证JWT令牌
     *
     * @param token 待解析的JWT字符串
     * @return      包含所有声明信息的Claims对象
     * @throws JwtException 当令牌无效、过期或签名不匹配时抛出
     */
    public Claims parseToken(String token) {
        return Jwts.parser()
                .verifyWith(key)       // 设置验证密钥
                .build()               // 构建解析器
                .parseSignedClaims(token)  // 解析并验证签名
                .getPayload();         // 获取声明部分
    }

    /**
     * 验证JWT令牌有效性
     *
     * @param token 待验证的JWT字符串
     * @return      true表示有效，false表示无效
     */
    public boolean validateToken(String token) {
        try {
            parseToken(token);  // 尝试解析令牌
            return true;        // 无异常表示有效
        } catch (JwtException | IllegalArgumentException e) {
            // 捕获以下异常：
            // - JwtException: 令牌过期、签名无效等
            // - IllegalArgumentException: 令牌为空或格式错误
            return false;
        }
    }
    /**
     * 从令牌中获取主题（通常为用户ID）
     *
     * @param token JWT字符串
     * @return      主题内容
     */
    public String getSubject(String token) {
        return parseToken(token).getSubject();
    }

    /**
     * 刷新令牌（生成新令牌，保留原声明信息）
     *
     * @param token 原JWT字符串
     * @return      新生成的JWT字符串
     */
    public String refreshToken(String token) {
        Claims claims = parseToken(token);  // 解析原令牌声明
        // 使用原主题和声明生成新令牌（更新签发时间和过期时间）
        return generateToken(claims.getSubject(), claims);
    }

    /**
     * 计算令牌剩余有效时间
     *
     * @param token JWT字符串
     * @return      剩余毫秒数（负数表示已过期）
     */
    public long getRemainingTime(String token) {
        Date expiration = parseToken(token).getExpiration();  // 获取过期时间
        return expiration.getTime() - System.currentTimeMillis();
    }
}
